home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / frontend.c < prev    next >
C/C++ Source or Header  |  1992-12-03  |  38KB  |  1,142 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                            HPACK Multi-System Archiver                        *
  4. *                            ===========================                        *
  5. *                                                                            *
  6. *                                Main Archiver Code                            *
  7. *                            FRONTEND.C  Updated 28/08/92                    *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                      and if you do so there will be....trubble.                *
  12. *                 And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *        Copyright 1989 - 1992  Peter C.Gutmann.  All rights reserved        *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. #include <ctype.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #ifdef __MAC__
  22.   #include "defs.h"
  23.   #include "arcdir.h"
  24.   #include "choice.h"
  25.   #include "error.h"
  26.   #include "filesys.h"
  27.   #include "flags.h"
  28.   #include "frontend.h"
  29.   #include "hpacklib.h"
  30.   #include "hpaktext.h"
  31.   #include "system.h"
  32.   #include "tags.h"
  33.   #include "wildcard.h"
  34.   #include "crypt.h"
  35.   #include "fastio.h"
  36.   #include "hpackio.h"
  37.   #include "store.h"
  38. #else
  39.   #include "defs.h"
  40.   #include "arcdir.h"
  41.   #include "choice.h"
  42.   #include "error.h"
  43.   #include "filesys.h"
  44.   #include "flags.h"
  45.   #include "frontend.h"
  46.   #include "hpacklib.h"
  47.   #include "system.h"
  48.   #include "tags.h"
  49.   #include "wildcard.h"
  50.   #include "crypt/crypt.h"
  51.   #include "io/fastio.h"
  52.   #include "io/hpackio.h"
  53.   #include "language/hpaktext.h"
  54.   #include "store/store.h"
  55. #endif /* __MAC__ */
  56.  
  57. /* Prototypes for functions in VIEWFILE.C */
  58.  
  59. void listFiles( void );
  60.  
  61. /* Prototypes for functions in ARCHIVE.C */
  62.  
  63. void addData( const char *filePath, const FILEINFO *fileInfoPtr, const WORD dirIndex, const FD dataFD );
  64. void retrieveData( FILEHDRLIST *fileInfo );
  65.  
  66. BOOLEAN confirmSkip( const char *str1, const char *str2, const BOOLEAN str1fileName );
  67.  
  68. void initBadFileInfo( void );
  69. void processBadFileInfo( void );
  70.  
  71. #ifdef __AMIGA__
  72.  
  73. /* Prototypes for functions defined in AMIGA.C */
  74.  
  75. LONG storeDiskObject( const char *pathName, const FD outFD );
  76. void storeComment( const FD outFD );
  77. #endif /* __AMIGA__ */
  78. #ifdef __OS2__
  79.  
  80. /* Prototypes for functions in OS2.C */
  81.  
  82. LONG storeEAinfo( const BOOLEAN isFile, const char *pathName, const LONG eaSize, const FD archiveFD );
  83.  
  84. #endif /* __OS2__ */
  85.  
  86. /* The following are defined in ARCHIVE.C */
  87.  
  88. extern BOOLEAN overWriteEntry;    /* Whether we overwrite the existing file
  89.                                    entry or add a new one in addFileHeader()
  90.                                    - used by FRESHEN, REPLACE, UPDATE */
  91.  
  92. /* The following are defined in ARCDIRIO.C */
  93.  
  94. extern int cryptFileDataLength;    /* The size of the encryption header at the
  95.                                    start of the compressed data */
  96.  
  97. /* The extensions used for HPACK archives, and used to match HPACK archives */
  98.  
  99. #if defined( __ATARI__ ) || defined( __MSDOS__ )
  100.   char HPAK_EXT[] = ".HPK";
  101. #elif defined( __ARC__ )
  102.   char HPAK_EXT[] = "";
  103. #else
  104.   char HPAK_EXT[] = ".hpk";
  105. #endif /* __MSDOS__ */
  106. #ifdef __ARC__
  107.   char HPAK_MATCH_EXT[] = "";
  108. #else
  109.   char HPAK_MATCH_EXT[] = ".\04Hh\06\04Pp\06\04Kk\06";    /* Compiled ".[Hh][Pp][Kk]" */
  110. #endif /* __ARC__ */
  111.  
  112. /* The distance in bytes to the next piece of data to handle */
  113.  
  114. long skipDist;
  115.  
  116. /* The name of the archive currently being processed */
  117.  
  118. char archiveFileName[ MAX_PATH ];
  119.  
  120. /* Some general vars used throughout HPACK */
  121.  
  122. char choice;                /* The command to the archiver */
  123. WORD flags = 0;                /* Various flags set by the user */
  124. WORD dirFlags = 0;            /* Directory-handling flags set by the user */
  125. WORD overwriteFlags = 0;    /* Overwrite-on-extract flags */
  126. WORD viewFlags = 0;            /* Options for the View command */
  127. WORD xlateFlags = 0;        /* Options for output translation */
  128. WORD cryptFlags = 0;        /* Options for encryption/security */
  129. WORD multipartFlags = 0;    /* Options for multipart read/write control */
  130. WORD sysSpecFlags = 0;        /* System-specific options */
  131. WORD commentType = TYPE_NORMAL;    /* The type of the archive comment */
  132. BOOLEAN archiveChanged = FALSE;    /* Whether the archive has been changed */
  133. BOOLEAN firstFile = TRUE;    /* Whether this is first file we're processing */
  134. char basePath[ MAX_PATH ];    /* The output directory given by the -b option */
  135. int basePathLen = 0;        /* The length of the basePath */
  136. int screenHeight, screenWidth;    /* The screen size */
  137. char *signerID;                /* userID of archive signer */
  138. char *mainUserID = NULL;    /* userID of main PKE destination (needs to be init'd) */
  139. char *secUserID;            /* userID of secondary PKE destination */
  140.  
  141. /****************************************************************************
  142. *                                                                            *
  143. *                        Support Routines for the Main Code                    *
  144. *                                                                            *
  145. ****************************************************************************/
  146.  
  147. /* Keep a record of every archive processed and don't do the same archive
  148.    again.  This detail is necessary due to a combination of local OS file
  149.    handling and HPACK file handling.  In the cases where HPACK works on
  150.    a second file which it then renames as the original after deleting the
  151.    original, the findNext() call may then match this newly-created archive
  152.    and process it again, which results in either a "Nothing to do" error
  153.    (for DELETE, FRESHEN, and UPDATE), or an endless loop as we keep matching
  154.    the new archives (for REPLACE) */
  155.  
  156. typedef struct FL {
  157.                   char *name;            /* This archive's name */
  158.                   struct FL *next;        /* Pointer to next record */
  159.                   } FILEDATA;
  160.  
  161. static FILEDATA *archiveNameListHead = NULL;
  162.  
  163. BOOLEAN addArchiveName( char *archiveName )
  164.     {
  165.     FILEDATA *namePtr, *prevPtr, *newRecord;
  166.  
  167.     for( namePtr = archiveNameListHead; namePtr != NULL; \
  168.                     prevPtr = namePtr, namePtr = namePtr->next )
  169.         /* Check whether we've already processed an archive of this name */
  170.         if( !strcmp( archiveName, namePtr->name ) )
  171.             return( TRUE );
  172.  
  173.     /* We have reached the end of the list without a match, so we add this
  174.        archive name to the list */
  175.     if( ( newRecord = ( FILEDATA * ) hmalloc( sizeof( FILEDATA ) ) ) == NULL || \
  176.         ( newRecord->name = ( char * ) hmalloc( strlen( archiveName ) + 1 ) ) == NULL )
  177.         error( OUT_OF_MEMORY );
  178.     strcpy( newRecord->name, archiveName );
  179.     newRecord->next = NULL;
  180.     if( archiveNameListHead == NULL )
  181.         archiveNameListHead = newRecord;
  182.     else
  183.         prevPtr->next = newRecord;
  184.     return( FALSE );
  185.     }
  186.  
  187. #if !defined( __MSDOS__ ) || defined( GUI )
  188.  
  189. /* Free up the memory used by the list of archive names */
  190.  
  191. void freeArchiveNames( void )
  192.     {
  193.     FILEDATA *namePtr = archiveNameListHead, *headerPtr;
  194.  
  195.     /* A simpler implementation of the following would be:
  196.        for( namePtr = archiveNameListHead; namePtr != NULL; namePtr = namePtr->next )
  197.            hfree( namePtr );
  198.        However this will fail on some systems since we will be trying to
  199.        read namePtr->next out of a record we have just free()'d */
  200.     while( namePtr != NULL )
  201.         {
  202.         headerPtr = namePtr;
  203.         namePtr = namePtr->next;
  204.         hfree( headerPtr->name );
  205.         hfree( headerPtr );
  206.         }
  207.     }
  208. #endif /* !__MSDOS__ || GUI */
  209.  
  210. /* Keep a record of every file added to an archive.  This is used by the
  211.    move option, which is a two-pass operation: The first pass adds the files
  212.    to the archive, the second pass goes through and deletes the added files */
  213.  
  214. static FILEDATA *filePathListHead = NULL, *prevPtr;
  215.  
  216. static void addFilePath( char *filePath )
  217.     {
  218.     FILEDATA *newRecord;
  219.  
  220.     /* Add the filepath to the list of filepaths */
  221.     if( ( newRecord = ( FILEDATA * ) hmalloc( sizeof( FILEDATA ) ) ) == NULL || \
  222.         ( newRecord->name = ( char * ) hmalloc( strlen( filePath ) + 1 ) ) == NULL )
  223.         error( OUT_OF_MEMORY );
  224.     strcpy( newRecord->name, filePath );
  225.     newRecord->next = NULL;
  226.     if( filePathListHead == NULL )
  227.         filePathListHead = newRecord;
  228.     else
  229.         prevPtr->next = newRecord;
  230.     prevPtr = newRecord;
  231.     }
  232.  
  233. /* Delete files and free up the memory used by the list of file paths */
  234.  
  235. void wipeFilePaths( void )
  236.     {
  237.     FILEDATA *namePtr = filePathListHead, *headerPtr;
  238.  
  239.     /* Step through the list of names freeing them */
  240.     while( namePtr != NULL )
  241.         {
  242.         headerPtr = namePtr;
  243.         namePtr = namePtr->next;
  244.  
  245.         /* Wipe the file */
  246.         wipeFile( headerPtr->name );
  247.  
  248.         /* Free the memory */
  249.         hfree( headerPtr->name );
  250.         hfree( headerPtr );
  251.         }
  252.     }
  253.  
  254. /* Add a series of directories to an archive (used by processDir()) */
  255.  
  256. static void addDirPath( char *fullPath, char *internalPath, const int currPathLen, const WORD dirIndex )
  257.     {
  258.     FILEINFO fileInfo;
  259.     int matchLen, count;
  260.     char ch;
  261.  
  262.     /* We shouldn't get here if the DIR_NOCREATE flags is set to specify that
  263.        no directories should be created if they don't already exist */
  264.     if( dirFlags & DIR_NOCREATE )
  265.         {
  266.         fullPath[ currPathLen ] = '\0';        /* Truncate to pure path */
  267.         error( PATH_NOT_FOUND, internalPath );
  268.         }
  269.  
  270.     setArchiveCwd( dirIndex );
  271.  
  272.     /* Grovel through all directories in the path, adding them to the
  273.        archive directory tree as we go */
  274.     matchLen = strlen( getPath( dirIndex ) );
  275.     while( matchLen < currPathLen )
  276.         {
  277.         /* Get the next directories name */
  278.         for( count = matchLen + 1; internalPath[ count ] && internalPath[ count ] != SLASH;
  279.              count++ );
  280.         ch = internalPath[ count ];
  281.         internalPath[ count ] = '\0';
  282.  
  283.         /* Try and add the new directory */
  284.         if( findFirst( fullPath, ALLFILES_DIRS, &fileInfo ) && isDirectory( fileInfo ) )
  285.             {
  286. #ifndef GUI
  287.             hprintfs( MESG_ADDING_DIRECTORY_s, fileInfo.fName );
  288. #endif /* !GUI */
  289.             addDirName( fileInfo.fName, fileInfo.fTime );
  290.             if( flags & STORE_ATTR )
  291.                 {
  292. #if defined( __MSDOS__ )
  293.                 addDirData( TAG_MSDOS_ATTR, TAGFORMAT_STORED, LEN_MSDOS_ATTR, LEN_NONE );
  294.                 fputDirByte( fileInfo.fAttr );        /* Save attributes */
  295. #elif defined( __AMIGA__ )
  296.                 addDirData( TAG_AMIGA_ATTR, TAGFORMAT_STORED, LEN_AMIGA_ATTR, LEN_NONE );
  297.                 fputDirByte( fileInfo.fAttr );        /* Save attributes */
  298.                 if( fileInfo.hasComment )
  299.                     storeComment( 0 );                /* Save comment */
  300.                 storeDiskObject( fullPath, 0 );
  301. #elif defined( __ARC__ )
  302.                 addDirData( TAG_ARC_ATTR, TAGFORMAT_STORED, LEN_ARC_ATTR, LEN_NONE );
  303.                 fputDirByte( fileInfo.fAttr );        /* Save attributes */
  304. #elif defined( __ATARI__ )
  305.                 addDirData( TAG_ATARI_ATTR, TAGFORMAT_STORED, LEN_ATARI_ATTR, LEN_NONE );
  306.                 fputDirByte( fileInfo.fAttr );        /* Save attributes */
  307. #elif defined( __MAC__ )
  308.                 addDirData( TAG_MAC_ATTR, TAGFORMAT_STORED, LEN_MAC_ATTR, LEN_NONE );
  309.                 fputDirWord( fileInfo.fAttr );        /* Save attributes */
  310.                 addDirData( TAG_CREATION_TIME, TAGFORMAT_STORED, LEN_CREATION_TIME, LEN_NONE );
  311.                 fputDirLong( fileInfo.createTime );    /* Save creation time */
  312.                 if( fileInfo.backupTime )
  313.                     {
  314.                     /* Save backup time if there is one */
  315.                     addDirData( TAG_BACKUP_TIME, TAGFORMAT_STORED, LEN_BACKUP_TIME, LEN_NONE );
  316.                     fputDirLong( fileInfo.backupTime );
  317.                     }
  318.                 if( fileInfo.versionNumber )
  319.                     {
  320.                     /* Save version number if there is one */
  321.                     addDirData( TAG_VERSION_NUMBER, TAGFORMAT_STORED, LEN_VERSION_NUMBER, LEN_NONE );
  322.                     fputDirWord( ( WORD ) fileInfo.versionNumber );
  323.                     }
  324. #elif defined( __OS2__ )
  325.                 if( fileInfo.aTime )
  326.                     {
  327.                     /* Save access time if there is one */
  328.                     addDirData( TAG_ACCESS_TIME, TAGFORMAT_STORED, LEN_ACCESS_TIME, LEN_NONE );
  329.                     fputDirLong( fileInfo.aTime );
  330.                     }
  331.                 if( fileInfo.cTime )
  332.                     {
  333.                     /* Save creation time if there is one */
  334.                     addDirData( TAG_CREATION_TIME, TAGFORMAT_STORED, LEN_CREATION_TIME, LEN_NONE );
  335.                     fputDirLong( fileInfo.cTime );
  336.                     }
  337.                 storeEAinfo( FALSE, fullPath, fileInfo.eaSize, archiveFD );
  338. #elif defined( __UNIX__ )
  339.                 addDirData( TAG_UNIX_ATTR, TAGFORMAT_STORED, LEN_UNIX_ATTR, LEN_NONE );
  340.                 fputDirWord( fileInfo.statInfo.st_mode );    /* Save attributes */
  341.                 addDirData( TAG_ACCESS_TIME, TAGFORMAT_STORED, LEN_ACCESS_TIME, LEN_NONE );
  342.                 fputDirLong( fileInfo.statInfo.st_atime );    /* Save access time */
  343. #endif /* Various OS-dependant attribute writes */
  344.                 }
  345.             internalPath[ count ] = ch;
  346.             matchLen = count;
  347.             findEnd( &fileInfo );
  348.             }
  349.         else
  350.             error( PATH_NOT_FOUND, internalPath );
  351.         }
  352.     }
  353.  
  354. /* Process one directories worth of files */
  355.  
  356. #define MAX_LEVELS        15        /* Max.directory depth we will recurse to */
  357.  
  358. static void processDir( FILENAMEINFO *fileNameList, const char *filePath, \
  359.                         const int internalPathStart )
  360.     {
  361.     FILEINFO fileInfo, currentDir[ MAX_LEVELS ];
  362.     FILENAMEINFO *fileNamePtr;
  363.     int currentLevel = 0, i;
  364.     int pathEnd[ MAX_LEVELS ];
  365.     char fullPath[ MAX_PATH ], *internalPath, ch;
  366.     int currPathLen = strlen( filePath ), internalPathLen, nameLen;
  367.     BOOLEAN foundFile, skipFile, skipFind;
  368. /*    BOOLEAN recurseFlags[ MAX_LEVELS ], forceRecurseFlags[ MAX_LEVELS ]; */
  369. /*    BOOLEAN doRecurse = ( flags & RECURSE_SUBDIR ) ? TRUE : FALSE; */
  370. /*    BOOLEAN recurseOnce = FALSE, forceRecurse = FALSE; */
  371.     WORD dirIndex = ROOT_DIR;    /* Default dir for no STORE_PATH option */
  372.     ATTR matchAttr = ( flags & STORE_ATTR ) ? ALLFILES_DIRS : FILES_DIRS;
  373.     FD workFD, savedInFD;
  374.  
  375.     /* Perform a depth-first search of all directories below the current
  376.        one.  Note that there is little advantage to be gained from an
  377.        explicit match rather than MATCH_ALL since we will at some point need
  378.        to match MATCH_ALL anyway to find directories; this also lets us use
  379.        extended wildcards when doing the string comparisons */
  380.     strcpy( fullPath, filePath );
  381.     internalPath = fullPath + internalPathStart;
  382.     internalPathLen = currPathLen - internalPathStart;
  383.     if( internalPathLen && !( internalPathLen == 1 && *internalPath == SLASH ) )
  384.         /* Append SLASH if necessary */
  385.         fullPath[ currPathLen++ ] = SLASH;
  386.     strcpy( fullPath + currPathLen, MATCH_ALL );
  387.     foundFile = skipFind = findFirst( fullPath, matchAttr, &fileInfo );
  388.  
  389.     while( TRUE )
  390.         if( foundFile && ( skipFind || findNext( &fileInfo ) ) )
  391.             {
  392.             foundFile = TRUE;
  393.             skipFind = FALSE;
  394.  
  395.             /* See if the file we have found is a directory */
  396.             if( isDirectory( fileInfo ) )
  397.                 {
  398. #if defined( __AMIGA__ ) || defined( __ATARI__ ) || defined( __MSDOS__ ) || \
  399.     defined( __OS2__ ) || defined( __UNIX__ )
  400.                 /* Continue if we've hit "." or ".." */
  401.                 if( *fileInfo.fName == '.' && ( !fileInfo.fName[ 1 ] || \
  402.                             ( fileInfo.fName[ 1 ] == '.' && !fileInfo.fName[ 2 ] ) ) )
  403.                     continue;
  404. #endif /* __AMIGA__ || __ATARI__ || __MSDOS__ || __OS2__ || __UNIX__ */
  405.  
  406. /*                if( recurseOnce || doRecurse ) */
  407.                 if( flags & RECURSE_SUBDIR )
  408.                     {
  409.                     /* Check the path length (the +5 is for the later
  410.                        addition of SLASH between the name and the path, and
  411.                        a SLASH_MATCH_ALL at the end) */
  412.                     if( ( ( nameLen = strlen( fileInfo.fName ) ) + currPathLen + 4 ) >= MAX_PATH )
  413.                         error( PATH_s_s_TOO_LONG, fullPath, fileInfo.fName );
  414.  
  415.                     /* Set up the information for the new directory */
  416. /*                    recurseFlags[ currentLevel ] = doRecurse; */
  417. /*                    recurseOnce = FALSE; */
  418.                     pathEnd[ currentLevel ] = currPathLen;
  419.                     currentDir[ currentLevel++ ] = fileInfo;
  420.                     strcpy( fullPath + currPathLen, fileInfo.fName );
  421.                     currPathLen += nameLen;
  422.                     if( flags & STORE_PATH )
  423.                         {
  424.                         /* Add the new directory if it isn't already in the archive */
  425.                         if( matchPath( internalPath, currPathLen - internalPathStart, \
  426.                                        &dirIndex ) != PATH_FULLMATCH )
  427.                             {
  428.                             /* Only add this path if we've been asked to add
  429.                                all paths anyway regardless of whether they contain
  430.                                any files */
  431.                             if( dirFlags & DIR_ALLPATHS )
  432.                                 {
  433.                                 addDirPath( fullPath, internalPath, \
  434.                                             currPathLen - internalPathStart, dirIndex );
  435.                                 archiveChanged = TRUE;
  436.                                 }
  437.                             }
  438.                         else
  439.                             {
  440.                             /* Go to that directory and update its timestamp */
  441.                             setArchiveCwd( dirIndex );
  442.                             setDirTime( dirIndex, fileInfo.fTime );
  443.                             }
  444.                         }
  445.  
  446. #ifndef GUI
  447.                     hprintfs( MESG_CHECKING_DIRECTORY_s, internalPath );
  448. #endif /* !GUI */
  449.                     if( currentLevel >= MAX_LEVELS )
  450.                         error( TOO_MANY_LEVELS_NESTING );
  451.                     strcpy( fullPath + currPathLen++, SLASH_MATCH_ALL );
  452.                     foundFile = skipFind = findFirst( fullPath, matchAttr, &fileInfo );
  453.                     continue;
  454.                     }
  455.                 else
  456.                     /* Make sure we don't mistake directories for filenames */
  457.                     continue;
  458.                 }
  459.  
  460.             /* Try and match the filename */
  461.             internalPathLen = currPathLen - internalPathStart;
  462.             for( fileNamePtr = fileNameList; fileNamePtr != NULL; \
  463.                  fileNamePtr = fileNamePtr->next )
  464. /*                if( ( forceRecurse && !isDirectory( fileInfo ) ) || \ */
  465.                 if( matchString( fileNamePtr->fileName, fileInfo.fName, fileNamePtr->hasWildcards ) )
  466.                     if( !( flags & STORE_PATH ) || \
  467.                         ( matchPath( internalPath, ( internalPathLen ) ? internalPathLen - 1 : 0, \
  468.                                                    &dirIndex ) ) == PATH_FULLMATCH )
  469.                         {
  470. /*                        \* Check whether what we've found is a directory */
  471. /*                        if( isDirectory( fileInfo ) ) */
  472. /*                            { */
  473. /*                            \* We've matched a directory, add the directory */
  474. /*                               info and recurse through it */
  475. /*                            skipFind = TRUE; */
  476. /*                            recurseOnce = forceRecurse = TRUE; */
  477. /*                            break;        /* Exit for loop */
  478. /*                            } */
  479.  
  480. retry:
  481.                         strcpy( fullPath + currPathLen, fileInfo.fName );
  482.  
  483.                         /* Make sure we don't try to archive the archive */
  484.                         skipFile = FALSE;
  485.                         if( ( ( i = strlen( fileInfo.fName ) - 3 ) > 1 ) && \
  486.                             ( ( ch = fileInfo.fName[ i ] ) == '$' || \
  487.                                 ch == 'H' || ch == 'h' ) )
  488.                             {
  489.                             /* First we perform a relatively cheap search
  490.                                for the HPAK_MATCH_EXT or "$$x" suffix to see
  491.                                if this is actually an archive file.  Only
  492.                                then will it be necessary to perform the
  493.                                expensive call to check if the files are identical */
  494.                             if( matchString( HPAK_MATCH_EXT, fileInfo.fName + i - 1, HAS_WILDCARDS ) || \
  495.                                 !strncmp( fileInfo.fName + i, "$$", 2 ) )
  496.                                 {
  497.                                 /* We've found an HPACK-related file; check
  498.                                    them to see if they are identical. The
  499.                                    following [ i + 2 ] is safe since we can
  500.                                    only arrive at this point if there is a
  501.                                    valid suffix at [ i + 2 ] */
  502.                                 switch( fileInfo.fName[ i + 2 ] )
  503.                                     {
  504.                                     case '1':
  505.                                         /* Extension '.$$1' */
  506.                                         if( errorFD != IO_ERROR )
  507.                                             skipFile = isSameFile( errorFileName, fullPath );
  508.                                         break;
  509.  
  510.                                     case '2':
  511.                                         /* Extension '.$$2' */
  512.                                         if( dirFileFD != IO_ERROR )
  513.                                             skipFile = isSameFile( dirFileName, fullPath );
  514.                                         break;
  515.  
  516.                                     case '3':
  517.                                         /* Extension '.$$3' */
  518.                                         if( secFileFD != IO_ERROR )
  519.                                             skipFile = isSameFile( secFileName, fullPath );
  520.                                         break;
  521.  
  522.                                     default:
  523.                                         /* Extension '.HPK' */
  524.                                         skipFile = isSameFile( archiveFileName, fullPath );
  525.                                     }
  526.                                 }
  527.                             }
  528.  
  529.                         if( skipFile )
  530.                             /* The file is an HPACK-related one, skip it */
  531.                             break;
  532.  
  533.                         /* Complain if the file is already in the archive and we
  534.                            try to ADD it.  Don't complain otherwise, since it will
  535.                            be replaced during pass 2 of the UPDATE command */
  536.                         if( addFileName( ( flags & STORE_PATH ) ? dirIndex : 0, \
  537.                                          commentType, fileInfo.fName ) == TRUE )
  538.                             {
  539.                             if( choice == ADD )
  540. #ifdef GUI
  541.                                 alert( ALERT_FILE_IN_ARCH, fileInfo.fName );
  542. #else
  543.                                 hprintf( MESG_FILE_s_ALREADY_IN_ARCH__SKIPPING, \
  544.                                                 fileInfo.fName );
  545. #endif /* GUI */
  546.                             break;    /* Exit the for loop */
  547.                             }
  548.  
  549.                         /* Ask the user whether they want to handle this file */
  550.                         if( ( flags & INTERACTIVE ) && \
  551.                               confirmSkip( MESG_ADD, fileInfo.fName, FALSE ) )
  552.                             {
  553.                             /* Remove the last filename entered from the name
  554.                                table since we since we won't be adding this file */
  555.                             deleteLastFileName();
  556.                             break;    /* Exit the for loop */
  557.                             }
  558.  
  559.                         if( ( workFD = hopen( fullPath, O_RDONLY | S_DENYWR | A_SEQ ) ) == IO_ERROR )
  560. #ifdef GUI
  561.                             alert( ALERT_CANNOT_OPEN_DATAFILE, internalPath );
  562. #else
  563.                             hprintf( WARN_CANNOT_OPEN_DATAFILE_s, internalPath );
  564. #endif /* GUI */
  565.                         else
  566.                             {
  567.                             savedInFD = getInputFD();
  568.                             setInputFD( workFD );
  569.                             addData( fullPath, &fileInfo, dirIndex, workFD );
  570.                             hclose( workFD );
  571.                             setInputFD( savedInFD );
  572.  
  573.                             /* If we're moving file, remember the name so we
  574.                                can go back and delete it later */
  575.                             if( flags & MOVE_FILES )
  576.                                 addFilePath( fullPath );
  577.                             }
  578.  
  579.                         break;    /* Exit the for loop */
  580.                         }
  581.                     else
  582.                         {
  583.                         /* Build the path to the file inside the archive.  The
  584.                            currPathLen check is in case of a trailing SLASH */
  585.                         addDirPath( fullPath, internalPath, ( internalPathLen ) ? \
  586.                                     internalPathLen - 1 : 0, dirIndex );
  587.  
  588.                         /* Restore the directory index and continue */
  589.                         dirIndex = currEntry;
  590.                         goto retry;
  591.                         }
  592.             }
  593.         else
  594.             /* Go back up one level if we can and try again */
  595.             if( currentLevel )
  596.                 {
  597.                 fullPath[ currPathLen ? currPathLen - 1 : 0 ] = '\0';
  598. #ifndef GUI
  599.                 hprintfs( MESG_LEAVING_DIRECTORY_s, internalPath );
  600. #endif /* !GUI */
  601.                 findEnd( &fileInfo );
  602.                 fileInfo = currentDir[ --currentLevel ];
  603.                 currPathLen = pathEnd[ currentLevel ];
  604. /*                forceRecurse = FALSE; */
  605. /*                doRecurse = recurseFlags[ currentLevel ]; */
  606.                 foundFile = TRUE;
  607.                 if( flags & STORE_PATH )
  608.                     popDir();
  609.                 }
  610.             else
  611.                 break;
  612.     }
  613.  
  614. /* Process a data file (with wildcards allowed) */
  615.  
  616. void handleArchive( void )
  617.     {
  618.     FD workFD, freshenFD;
  619.     FILEHDRLIST *prevPtr, *endPtr = NULL;
  620.     DIRHDRLIST *dirInfoPtr;
  621.     FILEINFO fileInfo;
  622.     FILEPATHINFO *pathInfoPtr;
  623.     FILENAMEINFO *fileInfoPtr;
  624.     char externalPath[ MAX_PATH ], *filePath, *fileName, *path;
  625.     FILEHDRLIST *list2currPtr, *list2startPtr = NULL;
  626.     int dummy;
  627.     BOOLEAN falseBasePath = FALSE, hasMatch, processedFile;
  628.     WORD dirNo;
  629.  
  630.     /* Reset the fast I/O subsystem */
  631.     resetFastOut();
  632.  
  633.     /* Make sure the base path is valid */
  634.     if( *basePath && !dirExists( basePath ) )
  635.         error( CANNOT_ACCESS_BASEDIR, basePath );
  636.  
  637.     /* Options which add files to an archive */
  638.     if( choice == ADD || choice == UPDATE )
  639.         {
  640.         /* Write ID string at start if necessary */
  641.         if( !htell( archiveFD ) )
  642.             {
  643.             memcpy( _outBuffer, HPACK_ID, HPACK_ID_SIZE );
  644.             _outByteCount = HPACK_ID_SIZE;
  645.             }
  646.  
  647.         /* Write encryption information */
  648.         if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  649.             cryptFileDataLength = cryptBegin( ( cryptFlags & CRYPT_SEC ) ? \
  650.                                               SECONDARY_KEY : MAIN_KEY );
  651.         else
  652.             cryptFileDataLength = 0;
  653.  
  654.         /* Add the external path component if there is one.  Note that
  655.            basePath already ends in a SLASH */
  656.         if( *basePath )
  657.             strcpy( externalPath, basePath );
  658.  
  659.         /* Process each group of specified file in subdirectories seperately.
  660.            We need to do things in this somewhat messy manner instead of
  661.            just passing <path>/<name> to findFirst in case we are asked to
  662.            do subdirectories as well */
  663.         for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
  664.              pathInfoPtr = pathInfoPtr->next )
  665.             {
  666.             /* Add the internal path component */
  667.             strcpy( externalPath + basePathLen, pathInfoPtr->filePath );
  668.  
  669. #ifdef __VMS__
  670.             /* If there is a node name attached to the path, make sure we
  671.                don't treat it as part of the file path */
  672.             if( pathInfoPtr->node != NULL )
  673.                 processDir( pathInfoPtr->fileNames, externalPath, \
  674.                             basePathLen + strlen( pathInfoPtr->node ) + \
  675.                             ( ( pathInfoPtr->device != NULL ) ? \
  676.                             strlen( pathInfoPtr->device ) : 0 ) );
  677.             else
  678. #endif /* __VMS__ */
  679.             /* If there is a device attached to the path, make sure we
  680.                don't treat it as part of the file path */
  681.             processDir( pathInfoPtr->fileNames, externalPath, \
  682.                         basePathLen + ( ( pathInfoPtr->device != NULL ) ? \
  683.                         strlen( pathInfoPtr->device ) : 0 ) );
  684.             }
  685.         if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  686.             cryptEnd();
  687.  
  688.         /* Write the archive directory and reset the error status if necessary */
  689.         if( archiveChanged )
  690.             {
  691.             writeArcDir();
  692.  
  693.             errorFD = IO_ERROR;
  694.             oldArcEnd = 0;
  695.             }
  696.  
  697.         return;
  698.         }
  699.  
  700.     /* Options which extract/change files in an archive */
  701.     if( choice == DELETE || choice == FRESHEN || choice == REPLACE )
  702.         {
  703.         /* Open a destination archive file.  Note that we can't use tmpnam()
  704.            since that would open a file in the current directory, which may
  705.            or may not be the same as the archive directory.  For the same
  706.            reason we can't make use of a preset directory for storing temporary
  707.            files (such as /tmp or getenv( "TMP" ) ).  So we open a file of the
  708.            form "archiveFileName." + TEMP_EXT */
  709.         strcpy( errorFileName, archiveFileName );
  710.         strcpy( errorFileName + strlen( errorFileName ) - 3, TEMP_EXT );
  711.         if( ( workFD = hcreat( errorFileName, CREAT_ATTR ) ) == IO_ERROR )
  712.             error( CANNOT_OPEN_TEMPFILE );
  713.         memcpy( _outBuffer, HPACK_ID, HPACK_ID_SIZE );    /* Put ID at start */
  714.         _outByteCount += HPACK_ID_SIZE;
  715.         errorFD = workFD;
  716.         setOutputFD( workFD );
  717.         }
  718.     else
  719.         if( choice == EXTRACT || choice == TEST )
  720.             /* Set up info for summary of files which didn't extract/test
  721.                properly */
  722.             initBadFileInfo();
  723.  
  724.     /* Select files and directories to process */
  725.     fileHdrCurrPtr = fileHdrStartPtr;
  726.     while( fileHdrCurrPtr != NULL )
  727.         {
  728.         /* Get all the info we need about the filename and path */
  729.         filePath = getPath( fileHdrCurrPtr->data.dirIndex );
  730.         fileName = fileHdrCurrPtr->fileName;
  731.         dirNo = fileHdrCurrPtr->data.dirIndex;
  732.         if( dirNo != ROOT_DIR )
  733.             /* Stomp final SLASH on non-root dir. */
  734.             filePath[ strlen( filePath ) - 1 ] = '\0';
  735.  
  736.         /* Try and match the filename and path against a command-line arg */
  737.         for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
  738.              pathInfoPtr = pathInfoPtr->next )
  739.             {
  740.             /* Get path information, skipping the initial SLASH if necessary */
  741.             path = pathInfoPtr->filePath;
  742.             if( *path == SLASH )
  743.                 path++;
  744.  
  745.             /* Try and match the filename and path against a given filespec */
  746.             for( fileInfoPtr = pathInfoPtr->fileNames; fileInfoPtr != NULL; \
  747.                  fileInfoPtr = fileInfoPtr->next )
  748.                 {
  749.                 if( matchString( fileInfoPtr->fileName, fileName, fileInfoPtr->hasWildcards ) && \
  750.                     ( ( flags & RECURSE_SUBDIR ) || \
  751.                       matchString( path, filePath, pathInfoPtr->hasWildcards ) ) && \
  752.                     ( fileHdrCurrPtr->hType ) == commentType )
  753.                     {
  754.                     /* Match on fileName/path, select it */
  755.                     fileHdrCurrPtr->tagged = TRUE;
  756.  
  757.                     /* Select the directories leading to it */
  758.                     if( ( dirNo != ROOT_DIR ) && ( flags & STORE_PATH ) )
  759.                         {
  760.                         /* Mark the encompassing directory for later creation */
  761.                         setDirTaggedStatus( dirNo );
  762.  
  763.                         /* Now walk up the tree to the root directory marking
  764.                            every directory we pass through unless we're creating
  765.                            only the containing directory */
  766.                         if( !( dirFlags & DIR_CONTAIN ) )
  767.                             while( ( dirNo = getParent( dirNo ) ) != ROOT_DIR && \
  768.                                    !getDirTaggedStatus( dirNo ) )
  769.                                 setDirTaggedStatus( dirNo );
  770.                         }
  771.  
  772.                     /* We've matched the name, exit the match loop */
  773.                     goto endNestedFileLoop;
  774.                     }
  775.                 }
  776.             }
  777.  
  778. endNestedFileLoop:
  779.         fileHdrCurrPtr = fileHdrCurrPtr->next;
  780.         }
  781.  
  782.     /* Now make a second pass selecting directories.  Unfortunately we
  783.        can't use the same early-out match scheme as before since there
  784.        may be multiple path spec to dir path matches, so we have to
  785.        check all path specs against the dir path */
  786.     for( dirNo = getFirstDir(); dirNo != END_MARKER; dirNo = getNextDir() )
  787.         {
  788.         /* Try and match the path to the directory */
  789.         filePath = getPath( dirNo );
  790.         if( dirNo != ROOT_DIR )
  791.             /* Stomp final SLASH on non-root dir */
  792.             filePath[ strlen( filePath ) - 1 ] = '\0';
  793.  
  794.         for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
  795.              pathInfoPtr = pathInfoPtr->next )
  796.             {
  797.             /* Get path information, skipping the initial SLASH if necessary */
  798.             path = pathInfoPtr->filePath;
  799.             if( *path == SLASH )
  800.                 path++;
  801.  
  802.             hasMatch = FALSE;
  803.             if( matchString( path, filePath, pathInfoPtr->hasWildcards ) )
  804.                 {
  805.                 /* Try and match the dir.name and path against a given filespec */
  806.                 for( fileInfoPtr = pathInfoPtr->fileNames; fileInfoPtr != NULL; \
  807.                      fileInfoPtr = fileInfoPtr->next )
  808.                     if( ( dirInfoPtr = getFirstDirEntry( dirNo ) ) != NULL )
  809.                         do
  810.                             if( matchString( fileInfoPtr->fileName, dirInfoPtr->dirName, \
  811.                                 fileInfoPtr->hasWildcards ) )
  812.                                 {
  813.                                 hasMatch = TRUE;
  814.                                 if( flags & RECURSE_SUBDIR )
  815.                                     /* Select all nested info */
  816.                                     selectDir( dirInfoPtr, TRUE );
  817.                                 else
  818.                                     if( fileInfoPtr->hasWildcards )
  819.                                         /* Select the directory itself */
  820.                                         setDirTaggedStatus( dirInfoPtr->dirIndex );
  821.                                     else
  822.                                         /* Select only immediate contents */
  823.                                         selectDirNoContents( dirInfoPtr->dirIndex, TRUE );
  824.                                 }
  825.                         while( ( dirInfoPtr = getNextDirEntry() ) != NULL );
  826.  
  827.                 /* The directory matched but there were no matching files
  828.                    within it, select the directory itself.  This occurs if
  829.                    the form /dir/ is used instead of /dir, to override the
  830.                    file selection */
  831.                 if( !hasMatch )
  832.                     setDirTaggedStatus( dirNo );
  833.                 }
  834.             }
  835.         }
  836.  
  837.     /* If we're asked to create all paths, mark all directories as selected */
  838.     if( dirFlags & DIR_ALLPATHS )
  839.         {
  840.         getFirstDir();                    /* Skip archive root directory */
  841.         for( dirNo = getNextDir(); dirNo != END_MARKER; dirNo = getNextDir() )
  842.             setDirTaggedStatus( dirNo );
  843.         }
  844.  
  845.  
  846.     /* If we're in unified compression mode, remember the last tagged file
  847.        header.  This is useful since it allows an early exit from the block
  848.        decompression when individual files are being extracted */
  849.     if( flags & BLOCK_MODE )
  850.         for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
  851.              fileHdrCurrPtr = fileHdrCurrPtr->next )
  852.             if( fileHdrCurrPtr->tagged )
  853.                 endPtr = fileHdrCurrPtr;
  854.  
  855. #ifndef GUI
  856.     /* Simple case: Display selected files/directories and exit */
  857.     if( choice == VIEW )
  858.         {
  859.         listFiles();
  860.         return;
  861.         }
  862. #endif /* !GUI */
  863.  
  864.     /* Recreate the directory tree in the archive if necessary */
  865.     if( ( choice == EXTRACT ) && ( flags & STORE_PATH ) )
  866.         makeDirTree();
  867.  
  868.     /* Flag the fact that we want to overwrite existing files headers rather
  869.        than add new ones when we call addData() */
  870.     overWriteEntry = TRUE;
  871.  
  872.     /* Force a read the first time we call getByte() */
  873.     forceRead();
  874.  
  875.     /* Process the encryption header if there is one */
  876.     if( ( choice == EXTRACT || choice == TEST || choice == DISPLAY ) && \
  877.         cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  878.         {
  879.         if( !cryptSetInput( getCoprDataLength(), &dummy ) )
  880.             error( CANNOT_PROCESS_CRYPT_ARCH );
  881.  
  882.         /* Skip the dummy header which covers the encryption info */
  883.         prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
  884.         }
  885.     else
  886.         prevPtr = fileHdrCurrPtr = fileHdrStartPtr;
  887.  
  888.     skipDist = 0L;
  889. again:
  890.     while( fileHdrCurrPtr != NULL )
  891.         {
  892.         processedFile = FALSE;
  893.         filePath = getPath( fileHdrCurrPtr->data.dirIndex );
  894.         if( fileHdrCurrPtr->tagged && fileHdrCurrPtr->hType == commentType )
  895.             {
  896.             if( choice == DELETE )
  897.                 {
  898.                 /* Ask for confirmation if necessary */
  899.                 if( flags & INTERACTIVE )
  900.                     if( confirmSkip( MESG_DELETE, buildInternalPath( fileHdrCurrPtr ), \
  901.                                      FALSE ) )
  902.                         goto skipData;
  903.  
  904.                 archiveChanged = TRUE;
  905. #ifndef GUI
  906.                 hprintfs( MESG_DELETING_s_FROM_ARCHIVE, \
  907.                           buildInternalPath( fileHdrCurrPtr ) );
  908. #endif /* GUI */
  909.                 skipDist += fileHdrCurrPtr->data.dataLen + \
  910.                             fileHdrCurrPtr->data.auxDataLen;
  911.  
  912. #if 0    /* !!!! Currently unused !!!! */
  913.                 /* Remove the header from the link chain */
  914.                 if( fileHdrCurrPtr->nextLink == NULL )
  915.                     {
  916.                     if( fileHdrCurrPtr->prevLink != NULL )
  917.                         {
  918.                         /* Delete header from end of chain */
  919.                         fileHdrCurrPtr->prevLink->nextLink = NULL;
  920.                         goto skipData;
  921.                         }
  922.                     }
  923.                 else
  924.                     if( fileHdrCurrPtr->prevLink == NULL )
  925.                         {
  926.                         /* Delete header from start of chain */
  927.                         fileHdrCurrPtr->nextLink->prevLink = NULL;
  928.                         goto skipData;
  929.                         }
  930.                     else
  931.                         {
  932.                         /* Delete header from middle of chain */
  933.                         fileHdrCurrPtr->nextLink->prevLink = NULL;
  934.                         fileHdrCurrPtr->prevLink->nextLink = NULL;
  935.                         goto skipData;
  936.                         }
  937. #endif /* 0 */
  938.  
  939.                 /* Remove the header for the skipped file from the header list */
  940.                 if( fileHdrCurrPtr == fileHdrStartPtr )
  941.                     {
  942.                     /* Special case for first header */
  943.                     prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
  944.                     deleteFileHeader( fileHdrStartPtr );
  945.                     fileHdrStartPtr = fileHdrCurrPtr;
  946.                     goto again;
  947.                     }
  948.                 else
  949.                     {
  950.                     prevPtr->next = fileHdrCurrPtr->next;
  951.                     deleteFileHeader( fileHdrCurrPtr );
  952.                     fileHdrCurrPtr = prevPtr;
  953.                     }
  954.  
  955.                 /* Flag the fact that we've processed a file in this pass */
  956.                 processedFile = TRUE;
  957.                 }
  958.             else
  959.                 if( choice == FRESHEN || choice == REPLACE )
  960.                     {
  961.                     /* Make sure the matched file is in the archive, that
  962.                        there is another copy on disk to freshen from, and
  963.                        that it's newer than the one in the archive */
  964.                     if( addFileName( fileHdrCurrPtr->data.dirIndex, \
  965.                                      fileHdrCurrPtr->hType, \
  966.                                      fileHdrCurrPtr->fileName ) == FALSE )
  967.                         goto endProcessFile;
  968.                     if( !( flags & STORE_PATH ) && !*basePath && *filePath )
  969.                         {
  970.                         /* File has a path within the archive but no STORE_PATH
  971.                            or explicit base path were given, create a false
  972.                            base path corresponding to the internal path */
  973.                         strcpy( basePath, filePath );
  974.                         basePathLen = strlen( basePath );
  975.                         falseBasePath = TRUE;
  976.                         }
  977.                     if( !findFirst( ( filePath = buildExternalPath( fileHdrCurrPtr ) ), \
  978.                                     ( flags & STORE_ATTR ) ? ALLFILES : FILES, &fileInfo ) )
  979.                         {
  980.                         findEnd( &fileInfo );
  981.                         goto endProcessFile;
  982.                         }
  983.                     findEnd( &fileInfo );
  984.                     if( ( choice == FRESHEN ) && \
  985.                         ( fileInfo.fTime <= fileHdrCurrPtr->data.fileTime ) )
  986.                         goto endProcessFile;
  987.  
  988.                     /* Ask for confirmation if necessary */
  989.                     if( flags & INTERACTIVE )
  990.                         if( confirmSkip( ( choice == FRESHEN ) ? MESG_FRESHEN : \
  991.                                                                  MESG_REPLACE, \
  992.                                          filePath, FALSE ) )
  993.                             goto endProcessFile;
  994.  
  995.                     archiveChanged = TRUE;
  996.  
  997.                     /* Add the new header data onto the end of list2 */
  998.                     if( list2startPtr == NULL )
  999.                         /* Set up list2 if it's not already set up */
  1000.                         list2startPtr = fileHdrCurrPtr;
  1001.                     else
  1002.                         list2currPtr->next = fileHdrCurrPtr;
  1003.                     list2currPtr = fileHdrCurrPtr;
  1004.  
  1005.                     /* Add the new data to the archive */
  1006.                     freshenFD = hopen( filePath, O_RDONLY | S_DENYWR | A_SEQ );
  1007.                     setInputFD( freshenFD );
  1008.                     addData( filePath, &fileInfo, fileHdrCurrPtr->data.dirIndex, freshenFD );
  1009.                     hclose( freshenFD );
  1010.                     setInputFD( archiveFD );
  1011.  
  1012.                     /* If we're moving file, remember the name so we can go
  1013.                        back and delete it later */
  1014.                     if( flags & MOVE_FILES )
  1015.                         addFilePath( filePath );
  1016.  
  1017.                     /* Unlink the header for the file to be updated */
  1018.                     if( fileHdrCurrPtr == fileHdrStartPtr )
  1019.                         {
  1020.                         /* Special case for first header */
  1021.                         prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
  1022.                         fileHdrStartPtr = fileHdrCurrPtr;
  1023.                         goto again;
  1024.                         }
  1025.                     else
  1026.                         {
  1027.                         prevPtr->next = fileHdrCurrPtr->next;
  1028.                         fileHdrCurrPtr = prevPtr;
  1029.                         }
  1030.  
  1031.                     /* Flag the fact that we've processed a file in this pass */
  1032.                     processedFile = TRUE;
  1033.                     }
  1034.                 else
  1035.                     {
  1036.                     /* Extract data */
  1037.                     skipToData();
  1038.                     retrieveData( fileHdrCurrPtr );
  1039.  
  1040.                     /* If there are no more files to decompress, exit now
  1041.                        (useful if BLOCK_MODE is set since we don't need to
  1042.                        process any data after this point) */
  1043.                     if( fileHdrCurrPtr == endPtr )
  1044.                         break;
  1045.                     }
  1046.             }
  1047.  
  1048. endProcessFile:
  1049.         /* Reset the false base path if necessary */
  1050.         if( falseBasePath )
  1051.             {
  1052.             *basePath = '\0';
  1053.             basePathLen = 0;
  1054.             falseBasePath = FALSE;
  1055.             }
  1056.  
  1057.         /* Check if there was a match for this file, and if we haven't
  1058.            already processed a file.  The second check is necessary since
  1059.            we may have deleted a file header as part of the operation
  1060.            performed, and fileHdrCurrPtr now points at another file header */
  1061.         if( !fileHdrCurrPtr->tagged && !processedFile )
  1062.             if( choice == DELETE )
  1063.                 {
  1064. skipData:
  1065.                 skipToData();
  1066.                 moveData( fileHdrCurrPtr->data.dataLen + fileHdrCurrPtr->data.auxDataLen );
  1067.                 }
  1068.             else
  1069.                 if( flags & BLOCK_MODE )
  1070.                     {
  1071.                     /* We're decompressing in block mode, we have to decompress
  1072.                        this file to keep the decompressor in sync, so we turn
  1073.                        the extract into a test */
  1074.                     choice = TEST;
  1075.                     skipToData();
  1076.                     retrieveData( fileHdrCurrPtr );
  1077.                     choice = EXTRACT;
  1078.                     }
  1079.                 else
  1080.                     /* Skip this archived file */
  1081.                     skipDist += fileHdrCurrPtr->data.dataLen + \
  1082.                                 fileHdrCurrPtr->data.auxDataLen;
  1083.  
  1084.         /* Step to next node in list */
  1085.         prevPtr = fileHdrCurrPtr;
  1086.         fileHdrCurrPtr = fileHdrCurrPtr->next;
  1087.         }
  1088.  
  1089.     /* Handle the archive update commands by moving any remaining data across
  1090.        to the new archive, concatenating the header list for this data onto
  1091.        the end of the one for the new archive, and then calling writeArcDir() */
  1092.     if( choice == FRESHEN || choice == REPLACE )
  1093.         {
  1094.         /* Step through the list of headers copying the data across as we go */
  1095.         for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
  1096.              fileHdrCurrPtr = fileHdrCurrPtr->next )
  1097.             {
  1098.             hlseek( archiveFD, fileHdrCurrPtr->offset + HPACK_ID_SIZE, SEEK_SET );
  1099.             moveData( fileHdrCurrPtr->data.dataLen + fileHdrCurrPtr->data.auxDataLen );
  1100.             }
  1101.  
  1102.         /* Concatenate the header list onto the end of the header list for the
  1103.            new archive if there is one */
  1104.         if( list2startPtr != NULL )
  1105.             {
  1106.             list2currPtr->next = fileHdrStartPtr;
  1107.             fileHdrStartPtr = list2startPtr;
  1108.             }
  1109.         }
  1110.  
  1111.     /* Perform cleanup for those functions that need it */
  1112.     if( choice == DELETE || choice == FRESHEN || choice == REPLACE )
  1113.         {
  1114.         /* Flush the data buffer */
  1115.         flushBuffer();
  1116.  
  1117.         /* Delete the old archive and rename the temp file to the archive
  1118.            name unless it's been deleted (which can happen if we delete all
  1119.            the files in the archive) */
  1120.         copyExtraInfo( archiveFileName, errorFileName );
  1121.         hclose( archiveFD );
  1122.         archiveFD = workFD;
  1123.         writeArcDir();
  1124.         hclose( archiveFD );
  1125.         hunlink( archiveFileName );
  1126.         if( errorFD != ERROR )
  1127.             hrename( errorFileName, archiveFileName );
  1128.  
  1129.         /* Flag the fact that we no longer need to attempt any recovery in
  1130.            case of an error */
  1131.         archiveFD = errorFD = dirFileFD = IO_ERROR;
  1132.         }
  1133.     else
  1134.         if( choice == EXTRACT || choice == TEST )
  1135.             /* If we were extracting or testing files, print a summary of
  1136.                corrupted files (since these often scroll off the screen too
  1137.                quickly to see) */
  1138.             processBadFileInfo();
  1139.  
  1140.     }    /* "Real programmers can write five-page-long do loops
  1141.             without difficulty" - _Real_Men_Don't_Program_Pascal_ */
  1142.